package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.order.strategy.TradeDataStrategy;
import com.atguigu.tingshu.order.strategy.factory.StrategyFactory;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
@RefreshScope
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private AlbumFeignClient albumFeignClient;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private OrderDerateMapper orderDerateMapper;
    @Autowired
    private AccountFeignClient accountFeignClient;
    @Autowired
    private StrategyFactory strategyFactory;
    @Autowired
    private RabbitService rabbitService;

    @Value("${order.cancel}")
    private int ttl;

    /**
     * 用户选择不同虚拟商品进行下单结算，封装渲染订单结算页所需要参数
     *
     * @param tradeVo
     * @return OrderInfoVo订单VO对象，将来订单提交时，需要将订单VO对象提交到服务端
     */
    @Override
    public OrderInfoVo tradeData(Long userId, TradeVo tradeVo) {
        OrderInfoVo orderInfoVo = new OrderInfoVo();

        //获取购买商品类别 ： 1001-专辑 1002 声音 1003 VIP
        String itemType = tradeVo.getItemType();
        TradeDataStrategy strategy = strategyFactory.getStrategy(itemType);
        strategy.handlerTradeData(tradeVo, orderInfoVo);
        orderInfoVo.setItemType(tradeVo.getItemType());

        //5.. 为订单vo其他杂项赋值 类别、流水号、时间戳
        //生成流水号存入redis 有效时间5分钟
        String tradeNo = IdUtil.randomUUID();
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        redisTemplate.opsForValue().set(tradeKey, tradeNo, RedisConstant.ORDER_TRADE_EXPIRE, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);

        //生成时间戳和签名
        orderInfoVo.setTimestamp(System.currentTimeMillis());
        Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo, false, true);
        String sign = SignHelper.getSign(orderInfoMap);
        orderInfoVo.setSign(sign);
        //6 响应订单vo
        return orderInfoVo;
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> submitOrder(Long userId, OrderInfoVo orderInfoVo) {
        //1.验证流水号
        //1.1 从redis中获取流水号
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //1.2 跟前端传递的流水号进行对比 使用lua脚本
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(script, Boolean.class);
        //1.2.1 验证成功，删除redis流水号
        Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());
        if (!flag) {
            //1.2.2 验证失败，响应签名错误（超时、篡改）
            throw new GuiguException(500, "流水号校验失败请重试");
        }

        //2.验证签名
        //2.1 将订单vo对象转换成map
        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo);
        //2.1 排除payway字段
        map.remove("payWay");
        SignHelper.checkSign(map);

        //业务校验通过
        //3.保存订单信息 订单明细 订单减免明细
        OrderInfo orderInfo = this.saveOrderInfo(orderInfoVo);

        String payWay = orderInfoVo.getPayWay();
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)){
            //远程调用账户服务扣减账户金额
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderInfo.getOrderNo());
            accountDeductVo.setUserId(orderInfo.getUserId());
            accountDeductVo.setAmount(orderInfo.getOrderAmount());
            accountDeductVo.setContent(orderInfo.getOrderTitle());
            Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
            if (!result.getCode().equals(200)){
                throw new GuiguException(500, "账户余额不足");
            }
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);
            // 远程调用用户服务虚拟发货
            // 构建虚拟发货vo对象
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList()
                    .stream()
                    .map(OrderDetailVo::getItemId)
                    .collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);

            result = userFeignClient.savePaidRecord(userPaidRecordVo);
            if (!result.getCode().equals(200)) {
                throw new GuiguException(500, "虚拟物品发货异常");
            }

        }
        //5.判断付款方式-如果是微信付款，采用延迟消息完成延迟关单
        if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay)) {
            rabbitService.sendDelayMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderInfo.getId(), ttl);

        }

        Map<String, String> map1 = new HashMap<>();
        map1.put("orderNo", orderInfo.getOrderNo());
        return map1;
    }

    /**
     * 保存订单信息
     * @param orderInfoVo
     * @return
     */
    public OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo) {
        //1.保存订单信息
        //1.1 将订单vo对象转换成实体对象
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);

        //1.2 手动设置订单属性
        //1.2.1 设置用户ID
        orderInfo.setUserId(AuthContextHolder.getUserId());
        //1.2.2 设置订单状态：未支付
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        //1.2.3 设置订单内容
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollUtil.isNotEmpty(orderDetailVoList)){
            orderInfo.setOrderTitle(orderDetailVoList.get(0).getItemName());
        }
        //1.2.4 设置订单编号 当日日期+分布式ID生成方案（雪花算法）
        String orderNo = DateUtil.today().replaceAll("-", "") + IdWorker.getIdStr();
        orderInfo.setOrderNo(orderNo);
        //1.3 保存订单 得到订单ID
        baseMapper.insert(orderInfo);
        Long orderId = orderInfo.getId();
        //2 保存订单明细列表
        if (CollUtil.isNotEmpty(orderDetailVoList)){
            orderDetailVoList
                    .stream()
                    .forEach(orderDetailVo -> {
                        OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
                        orderDetail.setOrderId(orderId);
                        orderDetailMapper.insert(orderDetail);
                    });

        }
        //3 保存订单减免明细列表
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (CollUtil.isNotEmpty(orderDerateVoList)){
            orderDerateVoList
                    .stream()
                    .forEach(orderDerateVo -> {
                        OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
                        orderDerate.setOrderId(orderId);
                        orderDerateMapper.insert(orderDerate);
                    });
        }
        return orderInfo;
    }

    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        //1.根据订单编号查询订单信息
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getOrderNo, orderNo);
        OrderInfo orderInfo = orderInfoMapper.selectOne(lambdaQueryWrapper);
        if (orderInfo != null){
            //2.根据订单ID查询订单明细列表

            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>()
                    .eq(OrderDetail::getOrderId, orderInfo.getId()));
            if (CollUtil.isNotEmpty(orderDetailList)){
                orderInfo.setOrderDetailList(orderDetailList);
            }
            //3.根据订单ID查询订单优惠列表
            List<OrderDerate> orderDerateList = orderDerateMapper.selectList(new LambdaQueryWrapper<OrderDerate>()
                    .eq(OrderDerate::getOrderId, orderInfo.getId()));
            if (CollUtil.isNotEmpty(orderDerateList)){
                orderInfo.setOrderDerateList(orderDerateList);
            }
        }

        return orderInfo;
    }

    /**
     * 查询当前用户订单列表
     * @param pageInfo
     * @param userId
     * @return
     */
    @Override
    public Page<OrderInfo> getOrderByPage(Long userId, Page<OrderInfo> pageInfo) {
       pageInfo = orderInfoMapper.getOrderByPage(userId, pageInfo);
       return pageInfo;
    }

    @Override
    public void cancelOrder(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo != null && SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())) {
            //2.1 设置订单状态为已取消
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            //2.2 更新订单信息
            orderInfoMapper.updateById(orderInfo);
            // todo 调用支付服务，修改本地交易记录状态为已关闭
        }
    }

    @Override
    public void orderPaySuccess(String orderNo) {
        //1.采用乐观锁修改订单状态改为已支付
        int count = baseMapper.updateOrderStatus(orderNo,SystemConstant.ORDER_STATUS_UNPAID, SystemConstant.ORDER_STATUS_PAID);
        if (count == 0 ){
            //2.todo 订单更新失败，主动查询支付状态，给用户发起退款
            throw new GuiguException(500, "订单更新失败"+ orderNo);
        }
        //3.远程调用用户服务完成虚拟发货
        OrderInfo orderInfo = this.getOrderInfo(orderNo);
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setOrderNo(orderNo);
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (CollUtil.isNotEmpty(orderDetailList)){
            List<Long> itemIdList = orderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);

            //2.2 判断业务状态码
            Result result = userFeignClient.savePaidRecord(userPaidRecordVo);
            if (!result.getCode().equals(200)) {
                throw new RuntimeException("远程调用-虚拟物品发货异常");
            }
            UserAccountDetail userAccountDetail = new UserAccountDetail();
            userAccountDetail.setUserId(orderInfo.getUserId());
            userAccountDetail.setTitle(orderInfo.getOrderTitle());
            userAccountDetail.setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_MINUS);
            userAccountDetail.setAmount(orderInfo.getOrderAmount());
            userAccountDetail.setOrderNo(orderNo);
            accountFeignClient.updateOrderDetail(userAccountDetail);
        }
    }

}
